﻿using ProjScan.Config;
using ProjScan.RecordManager;
using ProjScan.RecordManager.AssetData;
using ProjScan.ScanRule.Attr;
using ProjScan.Submodules.Art_Assets_Check;
using ProjScan.Tools;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.Rendering;

namespace ProjScan.ScanRule.ArtAssets.Mesh
{
    [ArtAssetRuleAttr]
    internal class UVPaddingTooSmall : RecordRuleImp<MeshRenderingAssetData, MeshRenderingRecordManager>
    {
        private int threshold;

        private string picDataFolder;

        protected override bool UseAssetDataCSVHeader => false;

        public UVPaddingTooSmall()
        {
            threshold = 4;
            picDataFolder = null;
            threshold = (Singleton<ConfigMgr>.Instance.GetConfig<ArtAssetsCheckConfig>() as ArtAssetsCheckConfig).uvPaddingThreshold;
            picDataFolder = ToolsHelper.GetScanOutPut() + "/UVPaddingTooSmall";
            if (!Directory.Exists(picDataFolder))
            {
                Directory.CreateDirectory(picDataFolder);
            }
        }

        public override bool CheckSupportability(out string msg)
        {
            if (!SystemInfo.supportsComputeShaders)
            {
                msg = "请将Standalone Windows平台的Graphics API设置成DX，有些Unity版本还需将Edit->Graphics Emulation设置为No Emulation。";
                return false;
            }

            if (Singleton<ScanInfo>.Instance.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || Singleton<ScanInfo>.Instance.graphicsDeviceType == GraphicsDeviceType.Direct3D12)
            {
                msg = "请将Standalone Windows平台的Graphics API设置成DX，有些Unity版本还需将Edit->Graphics Emulation设置为No Emulation。";
                return true;
            }

            msg = "请将Standalone Windows平台的Graphics API设置成DX，有些Unity版本还需将Edit->Graphics Emulation设置为No Emulation。";
            return false;
        }

        public override void CheckDirSetted()
        {
            if (SeparateDirSettingsEnabled)
            {
                if (target_configs == null || target_configs.Length == 0)
                {
                    ScanLog.UnityLogError("美术资源检测规则 " + GetType().Name + " 执行失败，未设置目标文件夹");
                }

                if (target_configs.Length == 1 && target_configs[0].assetPath.StartsWith("Assets/ArtCheckFolder"))
                {
                    ScanLog.UnityLogError("美术资源检测规则 " + GetType().Name + " 执行失败，未设置目标文件夹");
                }
            }
        }

        protected override string GetDetailCSVHeader()
        {
            return base.GetDetailCSVHeader() + ",UV边距检验结果图";
        }

        protected override string GetDetailInfo(MeshRenderingAssetData assetData, string failedInfo)
        {
            string str = string.IsNullOrEmpty(assetData.uvPaddingTooSmallPicPath) ? string.Empty : ("image://" + assetData.uvPaddingTooSmallPicPath);
            return base.GetDetailInfo(assetData, failedInfo) + str;
        }

        protected override bool IsOK(MeshRenderingAssetData assetData)
        {
            Texture2D guideline = null;
            bool flag;
            try
            {
                flag = !UVPaddingIsTooSmall(assetData.asset, out guideline, threshold);
            }
            catch (Exception param)
            {
                ScanLog.Instance.LogInfo(param, "UVPaddingTooSmall.UVPaddingIsTooSmall");
                return false;
            }

            if (!flag)
            {
                assetData.uvPaddingTooSmallPicPath = picDataFolder + "/" + assetData.id + ".png";
                ShaderUtility.SaveTexture(picDataFolder + "/" + assetData.id, guideline);
            }

            return flag;
        }

        public static bool UVPaddingIsTooSmall(UnityEngine.Mesh mesh, out Texture2D guideline, int threshold)
        {
            UnityEngine.Mesh mesh2 = GiveRandomColorToUvIsland(mesh);
            if (mesh2 == null)
            {
                guideline = null;
                return false;
            }

            RenderTexture renderTexture = ShaderUtility.RenderTextureFromMesh(mesh2, 512, "Hidden/UvColor");
            RenderTexture temporary = RenderTexture.GetTemporary(512, 512, 32, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
            temporary.enableRandomWrite = true;
            temporary.Create();
            ShaderUtility.LoadComputeShader().SetTexture(ShaderUtility.FetchKernelK3(), "v11", renderTexture);
            ShaderUtility.LoadComputeShader().SetTexture(ShaderUtility.FetchKernelK3(), "T1", temporary);
            ShaderUtility.LoadComputeShader().Dispatch(ShaderUtility.FetchKernelK3(), 64, 64, 1);
            renderTexture.Release();
            RenderTexture temporary2 = RenderTexture.GetTemporary(512, 512, 32, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
            temporary2.enableRandomWrite = true;
            temporary2.Create();
            ComputeBuffer computeBuffer = new ComputeBuffer(1, 4, ComputeBufferType.Counter);
            computeBuffer.SetCounterValue(0u);
            ShaderUtility.LoadComputeShader().SetBuffer(ShaderUtility.FetchKernelK4(), "v13", computeBuffer);
            ShaderUtility.LoadComputeShader().SetTexture(ShaderUtility.FetchKernelK4(), "v11", temporary);
            ShaderUtility.LoadComputeShader().SetTexture(ShaderUtility.FetchKernelK4(), "T1", temporary2);
            ShaderUtility.LoadComputeShader().Dispatch(ShaderUtility.FetchKernelK4(), 64, 64, 1);
            temporary.Release();
            ComputeBuffer computeBuffer2 = new ComputeBuffer(1, 4, ComputeBufferType.IndirectArguments);
            ComputeBuffer.CopyCount(computeBuffer, computeBuffer2, 0);
            int[] array = new int[1];
            computeBuffer2.GetData(array);
            computeBuffer2.Release();
            computeBuffer.Release();
            if (array[0] >= threshold)
            {
                RenderTexture active = RenderTexture.active;
                RenderTexture.active = temporary2;
                guideline = new Texture2D(512, 512);
                guideline.ReadPixels(new Rect(0f, 0f, 512f, 512f), 0, 0);
                guideline.Apply();
                RenderTexture.active = active;
                temporary2.Release();
                return true;
            }

            temporary2.Release();
            guideline = null;
            return false;
        }

        public static UnityEngine.Mesh GiveRandomColorToUvIsland(UnityEngine.Mesh mesh)
        {
            int[] triangles = mesh.triangles;
            Vector3[] vertices = mesh.vertices;
            Vector2[] uv = mesh.uv;
            if (vertices != null)
            {
                if (vertices.Length == 0)
                {
                    ScanLog.UnityLogError("(ArtCheck) Mesh: " + mesh.name + " vertices is empty.");
                    return null;
                }

                List<HashSet<int>> uvOverlappingVertices = GetUvOverlappingVertices(uv);
                List<HashSet<int>> vertexConnectivity = GetVertexConnectivityByTri(triangles);
                MergeVerticesConnectivityByOverlapping(uvOverlappingVertices, ref vertexConnectivity);
                Color[] array = new Color[vertices.Length];
                int num = Mathf.Max(10, vertexConnectivity.Count);
                ShaderUtility.LoadComputeShader().SetInt("sf", num);
                float num2 = 0.1f;
                foreach (HashSet<int> item in vertexConnectivity)
                {
                    Color color = UnityEngine.Random.ColorHSV(0f, 1f, 0f, 1f, 0.5f, 1f);
                    color.r = num2;
                    num2 += 1f / (float)num;
                    foreach (int item2 in item)
                    {
                        array[item2] = color;
                    }
                }

                UnityEngine.Mesh mesh2 = new UnityEngine.Mesh();
                List<Vector3> vertices2 = new List<Vector3>(vertices);
                List<Vector2> uvs = new List<Vector2>(uv);
                List<int> triangles2 = new List<int>(triangles);
                List<Color> colors = new List<Color>(array);
                mesh2.SetVertices(vertices2);
                mesh2.SetUVs(0, uvs);
                mesh2.SetTriangles(triangles2, 0);
                mesh2.SetColors(colors);
                return mesh2;
            }

            ScanLog.UnityLogError("(ArtCheck) Mesh: " + mesh.name + " vertices is null.");
            return null;
        }

        public static void MergeVerticesConnectivityByOverlapping(List<HashSet<int>> overlappingVertices, ref List<HashSet<int>> vertexConnectivity)
        {
            for (int i = 0; i < overlappingVertices.Count; i++)
            {
                for (int j = 0; j < vertexConnectivity.Count && !overlappingVertices[i].IsSubsetOf(vertexConnectivity[j]); j++)
                {
                    if (!overlappingVertices[i].Overlaps(vertexConnectivity[j]))
                    {
                        continue;
                    }

                    List<int> list = new List<int>();
                    for (int k = j + 1; k < vertexConnectivity.Count; k++)
                    {
                        if (overlappingVertices[i].Overlaps(vertexConnectivity[k]))
                        {
                            vertexConnectivity[j].UnionWith(vertexConnectivity[k]);
                            list.Add(k);
                        }
                    }

                    for (int num = list.Count - 1; num >= 0; num--)
                    {
                        vertexConnectivity.RemoveAt(list[num]);
                    }
                }
            }
        }

        public static List<HashSet<int>> GetVertexConnectivityByTri(int[] indices)
        {
            int num = indices.Length / 3;
            List<HashSet<int>> list = new List<HashSet<int>>();
            for (int i = 0; i < num; i++)
            {
                int item = indices[i * 3];
                int item2 = indices[i * 3 + 1];
                int item3 = indices[i * 3 + 2];
                bool flag = false;
                for (int j = 0; j < list.Count; j++)
                {
                    if (list[j].Contains(item) || list[j].Contains(item2) || list[j].Contains(item3))
                    {
                        list[j].Add(item);
                        list[j].Add(item2);
                        list[j].Add(item3);
                        flag = true;
                        break;
                    }
                }

                if (!flag)
                {
                    list.Add(new HashSet<int>
                    {
                        item,
                        item2,
                        item3
                    });
                }
            }

            return list;
        }

        public static List<HashSet<int>> GetUvOverlappingVertices(Vector2[] uvs)
        {
            List<KeyValuePair<int, uint>> list = new List<KeyValuePair<int, uint>>();
            for (int i = 0; i < uvs.Length; i++)
            {
                list.Add(new KeyValuePair<int, uint>(i, ShaderUtility.ManipulateVector(uvs[i])));
            }

            List<KeyValuePair<int, uint>> list2 = list.OrderBy((KeyValuePair<int, uint> item) => item.Value).ToList();
            List<HashSet<int>> list3 = new List<HashSet<int>>();
            for (int j = 0; j < list2.Count - 1; j++)
            {
                uint value = list2[j].Value;
                if (list2[j + 1].Value != value)
                {
                    continue;
                }

                HashSet<int> hashSet = new HashSet<int>();
                HashSet<int> hashSet2 = hashSet;
                int key = list2[j].Key;
                hashSet2.Add(key);
                HashSet<int> hashSet3 = hashSet;
                int key2 = list2[j + 1].Key;
                hashSet3.Add(key2);
                for (int k = j + 2; k < list2.Count && list2[k].Key == value; k++)
                {
                    HashSet<int> hashSet4 = hashSet;
                    int key3 = list2[k].Key;
                    hashSet4.Add(key3);
                    j++;
                }

                list3.Add(hashSet);
            }

            return list3;
        }
    }
}